local M = {}

--参考时间，选取以星期一作为起点的时间点，方便周计算（我们习惯性的以周一作为一周的起点，实际上周日才是）
local TIME_REFERENCE = os.time({year=2017, month=12, day=25, hour=0, min=0, sec=0})
local TIME_REFERENCE5 = os.time({year=2017, month=12, day=25, hour=5, min=0, sec=0})

--平年月份天数列表
local NotLeapYear_Month_Days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
--闰年月份天数列表
local LeapYear_Month_Days = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

---获取指定时间所处天数的精确时间
--@hour: 精确小时数
--@min: 精确分钟数
--@sec: 精确秒数
--@只有t的情况: day_start(t) == same_day_diff_hour_min_sec(t)
function M.same_day_diff_hour_min_sec(t, hour, min, sec)
    local ot = 0
    if hour then
        ot = ot + hour * 3600
    end
    if min then
        ot = ot + min * 60
    end
    if sec then
        ot = ot + sec
    end
    assert(ot < 86400, "same_day_diff_hour_min_sec error: seconds must less than 86400(1 day)")
    return t - ((t - TIME_REFERENCE) % 86400) + ot
end

---获取指定时间的今天起始时间
--@hour: 小时偏移
--@min: 分钟偏移
--@sec: 秒偏移
--@只有t的情况: day_start(t) == same_day_diff_hour_min_sec(t)
function M.day_start(t, hour, min, sec)
    local ot = 0
    if hour then
        ot = ot + hour * 3600
    end
    if min then
        ot = ot + min * 60
    end
    if sec then
        ot = ot + sec
    end
    assert(ot < 86400, "day_start error: seconds must less than 86400(1 day)")
    local delta = (t - TIME_REFERENCE) % 86400
    if delta < ot then
        ot = ot - 86400
    end
    return t - delta + ot
end

---获取指定时间的明天起始时间
function M.next_day_start(t, hour, min, sec)
    return M.day_start(t, hour, min, sec) + 86400
end

---判断指定两个时间是否是同一天
function M.is_same_day(t1, t2, hour, min, sec)
    return M.day_start(math.floor(t1), hour, min, sec) == M.day_start(math.floor(t2), hour, min, sec)
end

---获取指定时间的所属周起始时间
--@hour: 小时偏移
--@min: 分钟偏移
--@sec: 秒偏移
function M.week_start(t, hour, min, sec)
    local ot = 0
    if hour then
        ot = ot + hour * 3600
    end
    if min then
        ot = ot + min * 60
    end
    if sec then
        ot = ot + sec
    end
    assert(ot < 604800, "week_start error: seconds must less than 604800(7 day)")
    local delta = (t - TIME_REFERENCE) % 604800
    if delta < ot then
        ot = ot - 604800
    end
    return t - delta + ot
end

---获取指定时间的下周起始时间
function M.next_week_start(t, hour, min, sec)
    return M.week_start(t, hour, min, sec) + 604800
end

---判断指定两个时间是否是同一周
function M.is_same_week(t1, t2, hour, min, sec)
    return M.week_start(math.floor(t1), hour, min, sec) == M.week_start(math.floor(t2), hour, min, sec)
end

---获取指定时间所处天数的小时数
function M.day_hour(t)
    local delta = (t - TIME_REFERENCE) % 86400
    return math.floor(delta / 3600)
end

---获取指定时间所处小时的分钟数
function M.day_min(t)
    local delta = (t - TIME_REFERENCE) % 3600
    return math.floor(delta / 60)
end

---获取指定时间所处周的星期数(周一 = 1 ...)
function M.week_day(t)
    local delta = (t - TIME_REFERENCE) % 604800
    return math.floor(delta / 86400) + 1
end

---是否是闰年
function M.is_leap_year(year)
    return year % 100 > 0 and year % 4 == 0 or year % 400 == 0
end

---获取月份天数
function M.get_month_days(year, month)
    if M.is_leap_year(year) then
        return LeapYear_Month_Days[month] or error("get_month_days error: month must be a integer [1, 12]")
    else
        return NotLeapYear_Month_Days[month] or error("get_month_days error: month must be a integer [1, 12]")
    end
end

---获取指定时间的个月起始时间
function M.month_start(tim,hour)
    hour = hour or 0
    local year = os.date("%Y", tim)
    local month = os.date("%m", tim)
    local t=os.time({year=year, month=month, day=1, hour=hour})
    if t<=tim then
        return t
    end
    return os.time({year=year, month=month-1, day=1, hour=hour})
end

---获取指定时间的个月起始时间
function M.next_month_start(tim,hour)
    hour = hour or 0
    local year = os.date("%Y", tim)
    local month = os.date("%m", tim)
    local t=os.time({year=year, month=month, day=1, hour=hour})
    if t>tim then
        return t
    end
    return os.time({year=year, month=month+1, day=1, hour=hour})
end

function M.str2unix(str)
    if not str then
        return nil
    end

    local list={}
    string.gsub(str,"%d+",function(w)
        table.insert(list, tonumber(w) )
    end)

    if #list <= 0 then
        return nil
    end

    local tim = {
        year=(list[1] or 0 ),
        month=(list[2] or 0 ),
        day=(list[3] or 0 ),
        hour=(list[4] or 0 ),
        min=(list[5] or 0 ),
        sec=(list[6] or 0 ),
    }

    return os.time(tim)
end

function M.date_str(tim)
    tim=tim or os.time()
    return os.date("%Y-%m-%d %H:%M:%S",tim)
end

function M.day_str(tim)
    return tostring(os.date("%Y-%m-%d",tim))
end

function M.next_day_timediff(t,hour,min,sec)
    return M.next_day_start(t,hour,min,sec)-t
end

-- 第n周，以 TIME_REFERENCE 作为第1周，计算当前是第几周
function M.week_idx(now)
    now = now or os.time()
    return math.floor((now - TIME_REFERENCE)/604800) + 1
end

-- (以5点作为参考时间)第n周，以 TIME_REFERENCE5 作为第1周，计算当前是第几周
function M.week_idx_5(now)
    now = now or os.time()
    return math.floor((now - TIME_REFERENCE5)/604800) + 1
end

-- 第n天，以 TIME_REFERENCE 作为第1天，计算当前是第几天
function M.day_idx(now)
    now = now or os.time()
    return math.floor((now - TIME_REFERENCE)/86400) + 1
end

-- (以5点作为参考时间)第n天，以 TIME_REFERENCE5 作为第1天，计算当前是第几天
function M.day_idx_5(now)
    now = now or os.time()
    return math.floor((now - TIME_REFERENCE5)/86400) + 1
end

return M